package netty.rpc.server;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import netty.rpc.client.RpcRequest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.ApplicationContext;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * @author Jim
 * @version 1.0.0
 * @descripition:
 * @date 2022/5/11 0:37
 */
public class NettyRpcServerHandler extends ChannelInboundHandlerAdapter{

    private static final Logger logger = LogManager.getLogger(NettyRpcServerHandler.class);

    private ApplicationContext applicationContext;

    public NettyRpcServerHandler(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        RpcRequest rpcRequest = (RpcRequest) msg;
        logger.info("netty rpc server receives request: "+rpcRequest);

        RpcResponse rpcResponse = new RpcResponse();
        rpcResponse.setRequestId(rpcRequest.getRequestId());

        //解析请求 基于反射调用实现类
        try {
            Class<?> clazz = rpcRequest.getClazz();
            Map<String, ?> targetMap = applicationContext.getBeansOfType(clazz);
            // TODO 当前NettyRpc接口实现类  只允许存在一个
            // 后续可以基于版本号 进行多接口区分
            if (targetMap == null || targetMap.size() == 0){
                rpcResponse.setException(new ClassNotFoundException("class "+rpcRequest.getClazz().getName()+" not found"));
                rpcResponse.setSuccess(RpcResponse.FAILURE);
            }else if (targetMap.size() > 1){
                rpcResponse.setSuccess(RpcResponse.FAILURE);
                rpcResponse.setException(new RuntimeException("multiply class "+rpcRequest.getClazz().getName()+" found"));
            }else {
                Object target = targetMap.values().toArray()[0];
                Method method = clazz.getMethod(rpcRequest.getMethodName(), rpcRequest.getParameterTypes());
                Object result = method.invoke(target, rpcRequest.getArgs());

                rpcResponse.setResult(result);
                rpcResponse.setSuccess(RpcResponse.SUCCESS);
            }

        } catch (Exception e) {
           logger.error("netty rpc server failed to response the request."+e);
           rpcResponse.setSuccess(RpcResponse.FAILURE);
           rpcResponse.setException(e);
        }

        ctx.writeAndFlush(rpcResponse);
        logger.info("send rpc response to client: "+rpcResponse);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        logger.error("netty rpc channel inactive");
        super.channelInactive(ctx);
    }
}
